﻿using Newtonsoft.Json;
using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using Spire.Doc.Interface;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace OfficeHelper
{
	public class DocumentObjectConfig
	{
		public string Name { get; set; }
		public string Path { get; set; }
		public int TextBeginIndex { get; set; }
		public int TextEndIndex { get; set; }
		public string ConfigText { get; set; }
		public DocumentObjectType DocumentObjectType { get; set; }
		public KeyConfig KeyConfig { get; set; }

        public DocumentObjectConfig()
        {
            Path = "";
        }
	}

	public class WordTemplateConfig
	{
		public string FileName { get; set; }
        public string JsonFileName { get; set; }
        public char DataKeyMarker { get; set; }
        public string FileSavePath { get; set; }
        public JsonConfig JsonConfig { get; set; }
		public Dictionary<string, DocumentObjectConfig> DataKeys { get; set; }
        public IBuiltInFuncs BuiltInFuncs { get; set; }

        public WordTemplateConfig(string fileName, string jsonFileName = "", char dataKeyMarker = '$')
		{
            if(jsonFileName == "")
            {
                FileInfo file = new FileInfo(fileName);
                jsonFileName = file.FullName.Substring(0, file.FullName.LastIndexOf('.')) + ".json";
            }

			FileName = fileName;
            JsonFileName = jsonFileName;
			DataKeyMarker = dataKeyMarker;
            FileSavePath = @"pic/";
            BuiltInFuncs = new DefaultBuiltInFuncs();

            ParseJsonConfig();
            ParseWordTemplate();
		}

        private void ParseJsonConfig()
        {
            Dictionary<string, DocumentObjectConfig> data_keys = new Dictionary<string, DocumentObjectConfig>();
            Dictionary<string, DocumentObjectConfig> template_keys = new Dictionary<string, DocumentObjectConfig>();

            string json_content = Utils.GetTextFileContent(JsonFileName);
            JsonConfig json_config = JsonConvert.DeserializeObject<JsonConfig>(json_content);
            if(json_config.keys != null)
            {
                foreach(var key in json_config.keys)
                {
                    KeyConfig key_config = ParseKeyConfigJson(key.Value);
                    DocumentObjectConfig docObjConfig = new DocumentObjectConfig
                    {
                        Name = key.Key,
                        DocumentObjectType = key_config.DocumentObjectType,
                        ConfigText = key.Value.ToString(),
                        KeyConfig = key_config
                    };
                    data_keys.Add(key.Key, docObjConfig);
                }
            }

            JsonConfig = json_config;
            DataKeys = data_keys;
        }

		private void ParseWordTemplate()
		{
			// 读取文档
			Document doc = new Document();
			doc.LoadFromFile(FileName);

            for (int i = 0; i < doc.Sections.Count; i++)
			{
				Section sec = doc.Sections[i];
                ParseBody(sec.Body, AddPath("", i, "section"));
                ParseBody(sec.HeadersFooters.Header, AddPath("", i, "header"));
                ParseBody(sec.HeadersFooters.Footer, AddPath("", i, "footer"));
            }
            for (int i = 0; i < doc.TextBoxes.Count; i++)
            {
                TextBox box = doc.TextBoxes[i];
                ParseBody(box.Body, AddPath("", i, "textbox"));
            }

            doc.Dispose();
        }

		private string AddPath(string path, int index, string typeName)
		{
			return path + string.Format("{0}:{1},", index, typeName);
		}

		private void ParseBody(Body body, string path)
		{
			string tempPath;

			for (int i = 0; i < body.Paragraphs.Count; i++)
			{
				DocumentObject obj = body.Paragraphs[i];
				tempPath = AddPath(path, i, "para");
				ParseParagraph(obj as Paragraph, tempPath, path);
			}

			for (int i = 0; i < body.Tables.Count; i++)
			{
				ITable table = body.Tables[i];
				for (int m = 0; m < table.Rows.Count; m++)
				{
					for (int n = 0; n < table.Rows[m].Cells.Count; n++)
					{
						tempPath = AddPath(path, i, "table");
						tempPath = AddPath(tempPath, m, "row");
						tempPath = AddPath(tempPath, n, "cell");
						TableCell cell = table.Rows[m].Cells[n];
						ParseBody(cell, tempPath);
					}
				}
			}
		}

		private void ParseParagraph(Paragraph para, string path, string parentPath)
        {
            var hasKey = false;
            var keyText = "";

            var children = para.ChildObjects;
            foreach (DocumentObject child in children)
            {
                if (child.DocumentObjectType == DocumentObjectType.TextRange)
                {
                    TextRange textRange = child as TextRange;
                    if (textRange.CharacterFormat.UnderlineStyle == UnderlineStyle.Single)
                    {
                        // 如果是带下划线的文本
                        if (hasKey)
                        {
                            // 如果已经是hasKey为true的状态，说明当前文本属于key的一部分
                            keyText += textRange.Text;
                        }
                        else
                        {
                            // 如果还未处于hasKey为true的状态，判断如果首字符为DataKeyMarker，说明为key的起始位置
                            if (textRange.Text.Length > 0 && textRange.Text.First() == DataKeyMarker)
                            {
                                hasKey = true;
                                keyText = textRange.Text.Substring(1);
                            }
                        }
                    }
                    else
                    {
                        // 不带下划线，说明已经不属于key的一部分了
                        if (hasKey)
                        {
                            // 如果处于hasKey为true的状态，说明当前位置为key的结束位置，记录这个key
                            MarkDocumentObjectConfig(keyText, path, parentPath);
                        }
                        hasKey = false;
                    }
                }
            }
            if (hasKey)
            {
                MarkDocumentObjectConfig(keyText, path, parentPath);
            }
		}

        private void MarkDocumentObjectConfig(string keyText, string path, string parentPath)
        {
            string key = keyText.Trim();
            if (key != "" && DataKeys.ContainsKey(key))
            {
                // 有效的变量
                DocumentObjectConfig config = DataKeys[key];
                if (config.DocumentObjectType == DocumentObjectType.Body)
                {
                    config.Path += parentPath + ";";
                }
                else
                {
                    config.Path += path + ";";
                }
            }
            else
            {
                // 如果是个函数则执行函数，否则直接返回值
                string[] arr = key.Split('/');
                string funcKeyName = arr[0];
                if(arr.Length > 1)
                {
                    if (DataKeys.ContainsKey(funcKeyName))
                    {
                        string funcName = DataKeys[funcKeyName].KeyConfig.OutputFunc;
                        if (funcName == "")
                        {
                            throw new Exception(string.Format("\"{0}\"为变量而非函数", funcKeyName));
                        }
                        else
                        {
                            // 说明是个有效函数
                            // 检测变量是否存在
                            for (int i = 1; i < arr.Length; i++)
                            {
                                string temp = arr[i];
                                if (temp.Length > 0 && temp.First() == DataKeyMarker)
                                {
                                    // 说明是个变量
                                    temp = temp.Substring(1);
                                    if (!DataKeys.ContainsKey(temp))
                                    {
                                        throw new Exception(string.Format("\"{0}\"中存在无效的变量\"{1}\"", key, temp));
                                    }
                                    if (DataKeys[temp].KeyConfig.OutputFunc != "")
                                    {
                                        throw new Exception(string.Format("\"{0}\"中\"{1}\"为函数，应为变量", key, temp));
                                    }
                                }
                            }
                            // 说明函数和变量都正确，则添加这个key到配置中
                            DataKeys.Add(key, new DocumentObjectConfig
                            {
                                Name = key,
                                Path = path,
                                DocumentObjectType = DataKeys[funcKeyName].DocumentObjectType,
                                KeyConfig = DataKeys[funcKeyName].KeyConfig
                            });
                        }
                    }
                    else
                    {
                        throw new Exception(string.Format("无效的函数\"{0}\"", funcKeyName));
                    }
                }
                else
                {
                    throw new Exception(string.Format("无效的变量\"{0}\"", funcKeyName));
                }
            }
        }
        
        private KeyConfig ParseKeyConfigJson(Dictionary<string, string> dic)
        {
            KeyConfig key_config = Utils.DicToObj<KeyConfig>(dic);
            if(key_config.DocumentObjectType == DocumentObjectType.Picture)
            {
                key_config.DefaultValue = Utils.GetRelativeFilePath(JsonFileName, key_config.DefaultValue);
            }
            return key_config;
        }
    }

    public class JsonConfig
    {
        public Dictionary<string, Dictionary<string, string>> keys { get; set; }
    }

	public class KeyConfig
	{
		public string Name { get; set; }
		public string DefaultValue { get; set; }
		public string ValueFrom { get; set; }
		private string _type;
		public string Type {
			get
			{
				return _type;
			}
			set
			{
				_type = value.ToLower();
			}
		}
		public string ValueBeginMarker { get; set; }
		public string ValueEndMarker { get; set; }
		public int Width { get; set; }
		public int Height { get; set; }
		public string OutputFunc { get; set; }
		public bool Static { get; set; }
		public string Description { get; set; }
		public string Label { get; set; }
		public int Length { get; set; }
		public string Float { get; set; }

        public KeyConfig()
        {
            Name = "";
            DefaultValue = "";
            ValueFrom = "";
            Type = "textrange";
            ValueBeginMarker = "";
            ValueEndMarker = "";
            Width = 0;
            Height = 0;
            OutputFunc = "";
            Static = false;
            Description = "";
            Label = "";
			Length = 0;
			Float = "left";
        }

        public DocumentObjectType DocumentObjectType
		{
			get
			{
				switch (this.Type)
				{
					case "textrange":
						{
							return DocumentObjectType.TextRange;
						}
					case "picture":
						{
							return DocumentObjectType.Picture;
						}
					case "paragraph":
						{
							return DocumentObjectType.Paragraph;
                        }
                    case "body":
                        {
                            return DocumentObjectType.Body;
                        }
                    default:
						{
							return DocumentObjectType.TextRange;
						}
				}
			}
		}
	}
}
